package top.lixunda.ecommerce.server.business;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author lixunda
 * @version time:2020/3/18 12:36
 */
@SuppressWarnings({"SpringJavaAutowiredMembersInspection", "WeakerAccess"})
@Slf4j
public abstract class BaseBackupServiceImpl<M extends BaseMapper<T>, T, N extends BaseMapper<E>, E>
        extends BaseServiceImpl<M, T> implements IService<T> {

    @Autowired
    protected N backupMapper;

    public static final String TYPE_UPDATE = "UPDATE";

    public static final String TYPE_REMOVE = "REMOVE";

    /**
     * 数据转换
     *
     * @param t    原来的对象
     * @param type 保存类型，删除备份或更新备份
     * @return 缓存备份对象
     */
    protected abstract E convertData(T t, String type);

    /**
     * 从对象中获取主键id
     *
     * @param t 实体对象
     * @return 主键id
     */
    protected abstract Serializable findId(T t);

    @Override
    public boolean removeById(Serializable id) {
        T t = super.getById(id);
        if (t != null) {
            E e = this.convertData(t, TYPE_REMOVE);
            if (e != null) {
                this.backupMapper.insert(e);
            }
        }
        return super.removeById(id);
    }

    @Override
    public boolean removeByMap(Map<String, Object> columnMap) {
        Collection<T> list = super.listByMap(columnMap);
        if (list != null && list.size() > 0) {
            list.stream()
                    .map(item -> this.convertData(item, TYPE_REMOVE))
                    .filter(Objects::nonNull)
                    .parallel()
                    .forEach(item ->
                            this.backupMapper.insert(item));
        }
        return super.removeByMap(columnMap);
    }

    @Override
    public boolean remove(Wrapper<T> wrapper) {
        Collection<T> list = super.list(wrapper);
        if (list != null && list.size() > 0) {
            list.stream()
                    .map(item -> this.convertData(item, TYPE_REMOVE))
                    .filter(Objects::nonNull)
                    .parallel()
                    .forEach(item ->
                            this.backupMapper.insert(item));
        }
        return super.remove(wrapper);
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        Collection<T> list = super.listByIds(idList);
        if (list != null && list.size() > 0) {
            list.stream()
                    .map(item -> this.convertData(item, TYPE_REMOVE))
                    .filter(Objects::nonNull)
                    .parallel()
                    .forEach(item ->
                            this.backupMapper.insert(item));
        }
        return super.removeByIds(idList);
    }

    @Override
    public boolean updateById(T entity) {
        Optional.of(entity)
                .map(this::findId)
                .map(super::getById)
                .map(item -> this.convertData(item, TYPE_UPDATE))
                .ifPresent(item -> this.backupMapper.insert(item));
        return super.updateById(entity);
    }

    @Override
    public boolean update(T entity, Wrapper<T> updateWrapper) {
        Optional.of(updateWrapper)
                .map(item -> super.getOne(updateWrapper, false))
                .map(item -> this.convertData(item, TYPE_UPDATE))
                .ifPresent(item -> this.backupMapper.insert(item));
        return super.update(entity, updateWrapper);
    }

    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        Optional.of(entityList)
                .filter(item -> item.size() > 0)
                .map(item -> item.stream()
                        .map(this::findId)
                        .collect(Collectors.toList()))
                .map(super::listByIds)
                .map(item -> item.stream()
                        .map(item2 -> this.convertData(item2, TYPE_UPDATE))
                        .collect(Collectors.toList()))
                .ifPresent(item -> item.stream()
                        .parallel()
                        .forEach(item2 ->
                                this.backupMapper.insert(item2)));
        return super.updateBatchById(entityList, batchSize);
    }
}
